package ucla.axelia.modelo.dao;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Order;
import org.hibernate.validator.InvalidValue;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import ucla.axelia.exception.AxeliaException;
import ucla.axelia.modelo.data.DataGenerica;

public class ServicioGenericoDAO extends HibernateDaoSupport {
	
	// Atributos
	private Class clase;
	
	// Constructor
	public ServicioGenericoDAO(Class clase) {
		this.clase = clase;
	}

	// Metodos		
	protected void eliminarFisicamente(DataGenerica dataGenerica) {
		try {
			getHibernateTemplate().delete(dataGenerica);
        } catch (RuntimeException re) {
        	System.out.println(re);
            throw new RuntimeException("No se puede eliminar");
        }		
	}
	
	protected void eliminarLogicamente(DataGenerica dataGenerica, boolean eliminacionCascada) {
		if(eliminacionCascada){
			this.actualizarEstado(dataGenerica, DataGenerica.DATA_INACTIVO);
		}
		else{
			dataGenerica.setEstado(DataGenerica.DATA_INACTIVO);
		}
		getHibernateTemplate().saveOrUpdate(dataGenerica);		

	}

	protected void guardar(DataGenerica dataGenerica) {
		validate(dataGenerica);
		try {
			getHibernateTemplate().evict(dataGenerica);
			getHibernateTemplate().saveOrUpdate(dataGenerica);		
		} catch (RuntimeException re) {
			throw re;
		}
	}
	
	protected void restaurar(DataGenerica dataGenerica, boolean restauracionCascada) {
		try {
			if(restauracionCascada){
				this.actualizarEstado(dataGenerica, DataGenerica.DATA_ACTIVO);
			}
			else{
				dataGenerica.setEstado(DataGenerica.DATA_ACTIVO);
			}
			getHibernateTemplate().saveOrUpdate(dataGenerica);
		} catch (RuntimeException re) {
			throw re;
		}
	}
	
	protected DataGenerica buscarPorClave(Serializable clave) {
		try {
			DataGenerica data = (DataGenerica) getHibernateTemplate().get(clase, clave);
			if(data!=null){
				return data.getEstado().equals(DataGenerica.DATA_INACTIVO) ?  null : data;				
			}
			else{
				return null;
			}
		} catch (RuntimeException re) {
			throw re;
		}
	}
	
	protected List buscarPorEjemplo(DataGenerica dataGenerica, String estado) {
		try {			
			Session session = this.getSession(true);
			Criteria criteria = session.createCriteria(clase);
			criteria.add(Example.create(dataGenerica) );
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			return criteria.list();
		} catch (RuntimeException re) {
			throw re;
		}
	}
	
	protected List buscarPorEjemplo(DataGenerica dataGenerica, String estado, List orden) {
		try {			
			Session session = this.getSession(true);
			Criteria criteria = session.createCriteria(clase);
			criteria.add(Example.create(dataGenerica) );
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			for (Iterator iter = orden.iterator(); iter.hasNext();) {
				criteria.addOrder((Order)iter.next());				
			} 
			return criteria.list();
		} catch (RuntimeException re) {
			throw re;
		}
	}
	
	protected List buscarPorEjemplo(DataGenerica dataGenerica, String estado, int primero, int cantidad) {
		try {			
			Session session = this.getSession(true);
			Criteria criteria = session.createCriteria(clase).setFirstResult(primero).setMaxResults(cantidad);;
			criteria.add(Example.create(dataGenerica));
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			return criteria.list();
		} catch (RuntimeException re) {
			throw re;
		}
	}
	
	protected List buscarPorEjemplo(DataGenerica dataGenerica, String estado, int primero, int cantidad, List orden) {
		try {			
			Session session = this.getSession(true);
			Criteria criteria = session.createCriteria(clase).setFirstResult(primero).setMaxResults(cantidad);;
			criteria.add(Example.create(dataGenerica));
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			for (Iterator iter = orden.iterator(); iter.hasNext();) {
				criteria.addOrder((Order)iter.next());				
			} 
			return criteria.list();
		} catch (RuntimeException re) {
			throw re;
		}
	}
	
	public List buscarPorPropiedad(String nombre, Object valor, String estado) {
		try {
			Session session = this.getSession();
			Criteria criteria = session.createCriteria(clase);
			criteria.add(Expression.eq(nombre, valor));
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}						
			List lista = criteria.list();
			return lista;
		} catch (RuntimeException re) {
			throw re;
		}
	}
	
	public List buscarPorPropiedad(String nombre, Object valor, String estado, List orden) {
		try {
			Session session = this.getSession();
			Criteria criteria = session.createCriteria(clase);
			criteria.add(Expression.eq(nombre, valor));
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			for (Iterator iter = orden.iterator(); iter.hasNext();) {
				criteria.addOrder((Order)iter.next());				
			}
			List lista = criteria.list();
			return lista;
		} catch (RuntimeException re) {
			throw re;
		}
	}
	
	public List buscarPorPropiedad(String nombre, Object valor, String estado, int primero, int cantidad) {		
        try{
        	Session session = this.getSession(true);
        	Criteria criteria = session.createCriteria(clase).setFirstResult(primero).setMaxResults(cantidad);
            criteria.add(Expression.eq(nombre, valor));
            if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
            List lista = criteria.list();
            return lista;
        } catch (RuntimeException re) {
			throw re;
		}        
	}
	
	public List buscarPorPropiedad(String nombre, Object valor, String estado, int primero, int cantidad, List orden) {		
        try{
        	Session session = this.getSession(true);
        	Criteria criteria = session.createCriteria(clase).setFirstResult(primero).setMaxResults(cantidad);
            criteria.add(Expression.eq(nombre, valor));
            if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
            for (Iterator iter = orden.iterator(); iter.hasNext();) {
				criteria.addOrder((Order)iter.next());				
			}            
            List lista = criteria.list();
            return lista;
        } catch (RuntimeException re) {
			throw re;
		}        
	}
	
	public List buscarPorPropiedades(List criterios, String estado, List orden) {
	      
		try 
		{
			Session session = this.getSession();	
			Criteria criteria = session.createCriteria(clase);
			for (Iterator iter = criterios.iterator(); iter.hasNext();) {
				Criterion criterio = (Criterion) iter.next();
				criteria.add(criterio);
			}
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			for (Iterator iter = orden.iterator(); iter.hasNext();) {
				criteria.addOrder((Order)iter.next());				
			}
			List lista = criteria.list();
			//session.flush();
			//session.close();
			return lista;
		} 
		catch (RuntimeException re) 
		{	    	  
			throw re;
	    }		
	}
	
	public List buscarPorPropiedades(List criterios, String estado) {
		try {
			Session session = this.getSession();	
			Criteria criteria = session.createCriteria(clase);
			for (Iterator iter = criterios.iterator(); iter.hasNext();) {
				Criterion criterio = (Criterion) iter.next();
				criteria.add(criterio);
			}
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			List lista = criteria.list();
			return lista;
		} 
		catch (RuntimeException re) {	    	  
			throw re;
	    }		
	}
	
	public List buscarPorPropiedades(List criterios, String estado, int primero, int cantidad) {
	      
		try 
		{
			Session session = this.getSession();	
			Criteria criteria = session.createCriteria(clase).setFirstResult(primero).setMaxResults(cantidad);;
			for (Iterator iter = criterios.iterator(); iter.hasNext();) {
				Criterion criterio = (Criterion) iter.next();
				criteria.add(criterio);
			}
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			List lista = criteria.list();
			return lista;
		} 
		catch (RuntimeException re) 
		{	    	  
			throw re;
	    }		
	}
	
	public List buscarPorPropiedades(List criterios, String estado, int primero, int cantidad, List orden) {
	      
		try 
		{
			Session session = this.getSession();	
			Criteria criteria = session.createCriteria(clase).setFirstResult(primero).setMaxResults(cantidad);;
			for (Iterator iter = criterios.iterator(); iter.hasNext();) {
				Criterion criterio = (Criterion) iter.next();
				criteria.add(criterio);
			}
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			for (Iterator iter = orden.iterator(); iter.hasNext();) {
				criteria.addOrder((Order)iter.next());				
			}  
			List lista = criteria.list();
			return lista;
		} 
		catch (RuntimeException re) 
		{	    	  
			throw re;
	    }		
	}

	public List buscarIntervalo(String estado, int primero, int cantidad) {
		
		try{
			Session session = this.getSession(true);
            Criteria criteria = session.createCriteria(clase).setFirstResult(primero).setMaxResults(cantidad);
            if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
            return criteria.list();
        }
        catch (RuntimeException re){
        	throw re;
        }
	}
	
	public List buscarIntervalo(String estado, int primero, int cantidad, List orden) {
		
		try{
			Session session = this.getSession(true);
            Criteria criteria = session.createCriteria(clase).setFirstResult(primero).setMaxResults(cantidad);
            if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
            for (Iterator iter = orden.iterator(); iter.hasNext();) {
				criteria.addOrder((Order)iter.next());				
			} 
            return criteria.list();
        }
        catch (RuntimeException re){
        	throw re;
        }
	}
	
	public List buscarTodos(String estado) {
		try {			
			Session session = this.getSession(true);
			Criteria criteria = session.createCriteria(clase);
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			return criteria.list();
		} catch (RuntimeException re) {
			throw re;
		}
	}
	
	public List buscarTodos(String estado, List orden) {
		try {			
			Session session = this.getSession(true);
			Criteria criteria = session.createCriteria(clase);
			if(DataGenerica.DATA_ACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_ACTIVO));
			}
			else if(DataGenerica.DATA_INACTIVO.equals(estado)){
				criteria.add(Expression.eq(DataGenerica.ESTADO, DataGenerica.DATA_INACTIVO));
			}
			for (Iterator iter = orden.iterator(); iter.hasNext();) {
				criteria.addOrder((Order)iter.next());				
			} 
			return criteria.list();
		} catch (RuntimeException re) {
			throw re;
		}
	}
			
	public List buscarPorHQL(String hql) {
		//TODO Implementar metodo		
		Session session = super.getSession();
		Query query = session.createQuery(hql);
		List result = query.list();
		return result;		
	}
	
	public void validate(DataGenerica objeto) {
		
		InvalidValue[] validationMessages =  objeto.getValidador().getInvalidValues(objeto);
		StringBuffer errores = new StringBuffer();
		for (int i = 0; i < validationMessages.length; i++) {
			InvalidValue value = validationMessages[i];
			//errores.append(value.getPropertyName() + " - "+ value.getMessage() + "<br/>");
			errores.append(value.getMessage() + "<br/>");
		}
		if (errores.length() > 0) {
			throw new AxeliaException(errores.toString());
		}		
	}
	
	private void actualizarEstado(DataGenerica dataGenerica, String estado) {		
		try {
			dataGenerica.setEstado(estado);
			HashMap mapa = (HashMap)BeanUtils.describe(dataGenerica);
			Set listaCampos = mapa.keySet();
			for (Iterator iter = listaCampos.iterator(); iter.hasNext();) {
				String nombrePropiedad = (String) iter.next();
				Class clasePropiedad = PropertyUtils.getPropertyType(dataGenerica, nombrePropiedad);
				if("java.util.Set".equals(clasePropiedad.getName())){
					Set set = (Set)PropertyUtils.getProperty(dataGenerica, nombrePropiedad);
					for (Iterator iterator = set.iterator(); iterator.hasNext();) {
						DataGenerica dataGenericaAux= (DataGenerica) iterator.next();
						actualizarEstado(dataGenericaAux, estado);
					}
				}
			}			
		} catch (DataAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
}